﻿
Imports System.Threading
Imports osi.root.connector
Imports osi.root.envs
Imports osi.root.delegates
Imports osi.root.constants

Public Module spinwait
    Sub New()
        Dim i As Int32 = loops_per_yield
        Dim b As Boolean = single_cpu
        k_assert(Timeout.Infinite = -1)
    End Sub

    Public Function should_yield() As Boolean
        Return single_cpu
    End Function

    Public Function not_force_yield() As Boolean
        Return yield(False)
    End Function

    Public Function force_yield() As Boolean
        Return yield(True)
    End Function

    Public Function yield(Optional ByVal force As Boolean = True) As Boolean
        If force OrElse should_yield() Then
            Dim start_ms As Int64 = 0
            start_ms = environment_milliseconds()
            sleep(0)
            Return (environment_milliseconds() - start_ms) >= eighth_timeslice_length_ms
        Else
            Return False
        End If
    End Function

    Public Function try_wait_when(ByVal d As _do(Of Boolean),
                                  Optional ByVal ms As Int64 = npos,
                                  Optional ByRef wait_rounds As Int64 = 0,
                                  Optional ByRef yield_times As Int64 = 0) As Boolean
        If d Is Nothing Then
            Return False
        Else
            If ms < 0 Then
                ms = suggest_try_wait_length_ms
            End If
            wait_rounds = 0
            yield_times = 0
            Dim startMs As Int64 = 0
            startMs = environment_milliseconds()
            Dim rtn As Boolean = False
            rtn = d()
            While (environment_milliseconds() - startMs) < ms AndAlso rtn
                If not_force_yield() Then
                    non_overflow_increment(yield_times)
                End If
                non_overflow_increment(wait_rounds)
                rtn = d()
            End While

            Return rtn
        End If
    End Function

    Public Sub strict_wait_when(Of T)(ByVal d As _do(Of T, Boolean), ByRef o As T)
        If should_yield() Then
            lazy_wait_when(d, o)
        Else
#If DEBUG Then
            k_assert(Not d Is Nothing)
#End If
            While d(o)
            End While
        End If
    End Sub

    Public Sub wait_when(Of T)(ByVal d As _do(Of T, Boolean), ByRef o As T)
        If should_yield() Then
            lazy_wait_when(d, o)
        Else
#If DEBUG Then
            k_assert(Not d Is Nothing)
#End If
            Dim i As Int32 = 0
            While d(o)
                i += 1
                If i > loops_per_yield Then
                    If force_yield() Then
                        i = 0
                    Else
                        i = loops_per_yield
                    End If
                End If
            End While
        End If
    End Sub

    Public Sub lazy_wait_when(Of T)(ByVal d As _do(Of T, Boolean), ByRef o As T)
#If DEBUG Then
        k_assert(Not d Is Nothing)
#End If
        While d(o)
            force_yield()
        End While
    End Sub

    Public Sub strict_wait_when(ByVal d As _do(Of Boolean))
        k_assert(Not d Is Nothing)
        strict_wait_when(Function(ByRef x) d(), 0)
    End Sub

    Public Sub wait_when(ByVal d As _do(Of Boolean))
        k_assert(Not d Is Nothing)
        wait_when(Function(ByRef x) d(), 0)
    End Sub

    Public Sub lazy_wait_when(ByVal d As _do(Of Boolean))
        k_assert(Not d Is Nothing)
        lazy_wait_when(Function(ByRef x) d(), 0)
    End Sub

    Public Function yield_wait(ByVal e As WaitHandle, Optional ByVal wait_ms As Int32 = 8) As Boolean
        If e Is Nothing Then
            not_force_yield()
            Return False
        Else
            If wait_ms < Timeout.Infinite Then
                wait_ms = Timeout.Infinite
            End If
            If e.WaitOne(0) Then
                Return True
            Else
                not_force_yield()
                Return e.WaitOne(wait_ms)
            End If
        End If
    End Function
End Module
